#!/usr/bin/perl

# +-------------------------------------------------------------------------+
# | Script       : HbtParse_SeqToDB.pl                                      |
# | Version	     : 0.1b                                                     |
# | Author       : Rafael Henchen                                           |
# | Description  : This scripts scan .java or .xml files inside a specified |
# |                directory to parse for sequence generators and make a    |
# |                (sequence,table,column) association, if possible.        |
# +-------------------------------------------------------------------------+

# TODO:
# - Match sequence generator name from @GeneratedValue annotation
# - Parser of the <sequence-generator> XML element
# - Use array of sequences inside file parser (support multiple sequences per file)

use strict;

use File::Find::Rule;
use Getopt::Long;
use Config;
use DBI;

my $help;
my $debug;
my $directory;
my $type = 'annotation';
my $db_url;
my $db_user;
my $db_password;

my $fileExtension;
my $sequenceCount = 0;
my $matchcount = 0;

my $dbh;
my $insert_seq_h;

GetOptions(
	"d=s" => \$directory,
	"t=s" => \$type,
	"B=s" => \$db_url,
	"U=s" => \$db_user,
	"P=s" => \$db_password,
	"h"   => \$help,
	"debug" => \$debug
) or usage("Invalid commmand line options.");

usage("")
  unless not defined $help;
usage("The root directory (-d) must be specified.")
  unless defined $directory;
usage("$directory: No such file or directory")
  unless ( -d $directory );
usage("The database (-B) must be specified.")
  unless defined $db_url;
usage("The database user (-U) must be specified.")
  unless defined $db_user;

SWITCH: {
	if ($type eq 'annotation') { $fileExtension = '*.java'; last SWITCH; }
	if ($type eq 'xml-element') { $fileExtension = '*.xml'; last SWITCH; }
	usage("Type of parser '-t $type' is not supported");
}

while (!$db_password){
	print "Enter '$db_user' paswword:";
	chomp ($db_password = <STDIN>);
}

### Prepare DB Connection and Insert statement
if ($db_url =~ m/^dbi:Oracle/ig) {prepareDB_Oracle(); }
else { die "Database driver not supported yet" }

 

### Process all extension matched files in the directory
my @files = File::Find::Rule->file()->name($fileExtension)->in($directory);
foreach (@files) {
	SWITCH: {
		if ($type eq 'annotation') { parseAnnotation($_); last SWITCH; }
		if ($type eq 'xml-element') { parseXmlElement($_); last SWITCH; }
	}
}

## end
$dbh->disconnect;
print STDOUT ("Number of matches = $matchcount || sequences = $sequenceCount");
exit;




######################################### SUBS ##################################

### -----------------------------------------------
### Parser of the @SequenceGenerator annotation
### -----------------------------------------------
sub parseAnnotation {
	my ($file) = @_;
	($file =~ s/\//\\/g) if ($Config{osname} =~ m/MSWin/g);

	my $table_name;
	my $column_name;
	my $sequence_name;
	my $insideIdColumn = 0;
	
	my $fileSeq = 0;
	my $fileMatch = 0;
	
    # /DEBUG
    if (defined $debug){
		open F, $file
		  or print "Could not open file $file\n" && return;
		while (<F>) {
			if (m/\@SequenceGenerator/i){ 	
				$sequenceCount++;
				$fileSeq = $file;
			}
		}
		close F;
    }

	open F, $file
	  or print "Could not open file $file\n" && return;
	while (<F>) {
		if (m/[\@Table|\@Entity]\(name\s*=\s*"\s*(\w+)\s*"\)/ ) {
			$table_name = uc $1;
		}elsif (m/\@SequenceGenerator\(.*sequenceName\s*=\s*"\s*(\w+)\s*".*\)/){ 
			$sequence_name = uc $1; ## Find @SequenceGenerator
			last;
		}
	}
	close F;

	if ( defined $table_name && defined $sequence_name ) {
		open F, $file;
		while (<F>) {
			if (!$insideIdColumn){ ## First look for the @Id expression
				if (m/\s*\@Id\s+/){
					$insideIdColumn = 1;
					redo;
				}
			} else { 
				if (m/^\s*[private|public|protected].+[;|)|}]$/i){ ## @Id block ends with the Java attribute clause 
					last;
				}
			 	if (m/\@Column\(.*name\s*=\s*"\s*(\w+)\s*".*\)/){
			 		$column_name = uc $1; ## Find @Column
			 	}
			}
		}
		continue {
			if ( $column_name) { ## If @Id have @Column and @SequenceGenerator create association
				my $class_file = substr ($file, length ($directory) + 1); 
				$insert_seq_h->execute($sequence_name,$table_name,$column_name,$class_file)
					or print("WARNING: Couldn't insert sequence '$sequence_name' into AUX_SEQUENCE_TAB_COL from file '$class_file'\n");
				$matchcount++;
				($fileMatch = $file) if (defined $debug); # /DEBUG
				last;
			}
		}
		close F;
	}

    # /DEBUG
    if (defined $debug){
		if ($fileSeq and !$fileMatch ){ ## Verify if the REGEXs are Ok 
			print "File with \@SequenceGenerator and not match REGEX: $fileSeq\n";
		}
	}
	
}

### -----------------------------------------------
### Parser of the <sequence-generator> XML element
### -----------------------------------------------
sub parseXmlElement{
	die "Parser of the <sequence-generator> XML element not yet implemented";
}


### -----------------------------------------------
### prepare Oracle Database
### -----------------------------------------------
sub prepareDB_Oracle{
	$dbh = DBI->connect($db_url,$db_user,$db_password,{AutoCommit => 1})
  		or die "Couldn't connect to database: " . DBI->errstr;
  
    my @exist_aux =  $dbh->selectrow_array("SELECT count(1) as count FROM user_tables WHERE table_name = ?",undef,'AUX_SEQUENCE_TAB_COL')
    	or die "Couldn't prepare statement: " . $dbh->errstr;

	if ( $exist_aux[0] eq '0' ){
		$dbh->do(q{CREATE TABLE AUX_SEQUENCE_TAB_COL(SEQUENCE_NAME VARCHAR2(40) PRIMARY KEY, TABLE_NAME VARCHAR2(40) NOT NULL, COLUMN_NAME  VARCHAR2(40) NOT NULL, PARSED_FILE VARCHAR2(4000) NOT NULL) },undef)
			or die "Couldn't create auxiliary table: " . $dbh->errstr;
	}

	$insert_seq_h = $dbh->prepare("INSERT INTO AUX_SEQUENCE_TAB_COL(SEQUENCE_NAME,TABLE_NAME,COLUMN_NAME, PARSED_FILE) VALUES (?,?,?,?)")
		or print "Couldn't prepare statement: " . $dbh->errstr;
}


### -----------------------------------------------
### Print usage
### -----------------------------------------------
sub usage {
	my $message = $_[0];
	if ( defined $message && length $message ) {
		$message .= "\n"
		  unless $message =~ /\n$/;
	}

	my $command = $0;
	$command =~ s#^.*/##;

	print STDERR (
		$message,
		"USAGE: $command -d directory -B database -U user [-P password -t type]\n"
		  . " -d directory  \t\t Root directory to start looking for '.java' files\n"
		  . " -B database   \t\t Database source (driver deppendent) \n"
		  . "               \t\t  Ex.: for Oracle use \"dbi:Oracle://host1.local.com:1521/ORCL\"\n"
		  . " -U user       \t\t Database user\n"
		  . " -P password   \t\t Database paword\n"
		  . " -t type       \t\t Specify the type of the parser (default = annotation)\n"
		  . "               \t\t  Supported values: 'annotation', 'xml-element'\n"
		  . " -h            \t\t Help, show this message\n"
	);

	die("\n");
}

